﻿using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net;
using System.Security.Cryptography.X509Certificates;

namespace opentel
{
    using opentel.hub;
    using opentel.hub.data;
    using Org.BouncyCastle.Asn1.X509;
    using SIPSorcery.SIP;

    public class OpenTelServer
    {
        internal SIPTransport transport;
        internal X509Certificate2 certificate;
        internal IPAddress[] listen_addrs;
        internal int port;
        internal int port_tls;
        internal int port_rtp;
        internal IPAddress public_ipaddress = null;
        internal contactstore contact;
        internal callingstore calling;
        public OpenTelServer(int port, params IPAddress[] addrs)
        {
            listen_addrs = addrs;
            this.port = port;
            contact = new contactstore();
            calling = new callingstore();
            transport = new SIPTransport();
            foreach (var addr in LoopListenAddress())
            {
                transport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(addr, port)));
                transport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(addr, port)));
            }
            transport.SIPTransportRequestReceived += OnRequest;
            transport.SIPTransportResponseReceived += OnResponse;
        }
        /// <summary>
        /// 运行状态
        /// </summary>
        /// <remarks>判断是否已经关闭</remarks>
        public bool Running { 
            get
            {
                return !transport.m_closed;
            }
        }

        internal IEnumerable<IPAddress> LoopListenAddress()
        {
            for (var i = 0; i < listen_addrs.Length; i++) yield return listen_addrs[i];
        }
        /// 配置tls 监听
        public void set_tls_cert(X509Certificate2 cert, int port)
        {
            this.port_tls = port;
            foreach (var addr in LoopListenAddress()) transport.AddSIPChannel(new SIPTLSChannel(cert, new IPEndPoint(addr, port)));
        }
        private async Task OnResponse(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPResponse sipResponse)
        {
            var method = sipResponse.Header.CSeqMethod;
            var status = sipResponse.Status;

            switch (method)
            {
                case SIPMethodsEnum.INVITE:
                    await do_invite_rep(sipResponse);
                    break;
            }
        }

        private async Task OnRequest(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            try
            {
                if (sipRequest.Method != SIPMethodsEnum.REGISTER && check_auth(sipRequest))
                {
                    await send_unauthorised(sipRequest);
                }
                else switch (sipRequest.Method)
                {
                    case SIPMethodsEnum.REGISTER:
                        await do_register(localSIPEndPoint, remoteEndPoint, sipRequest);
                        break;
                    case SIPMethodsEnum.INVITE:
                        await do_invite_req(sipRequest);
                        break;
                    case SIPMethodsEnum.BYE:
                        await do_bye(sipRequest);
                        break;
                    case SIPMethodsEnum.ACK:
                        SIPResponse ackResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                        await transport.SendResponseAsync(ackResponse);
                        break;
                    case SIPMethodsEnum.CANCEL:
                        SIPResponse cancelResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                        await transport.SendResponseAsync(cancelResponse);
                        break;
                    case SIPMethodsEnum.OPTIONS:
                        SIPResponse optionsResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null);
                        await transport.SendResponseAsync(optionsResponse);
                        break;
                    case SIPMethodsEnum.SUBSCRIBE:
                        SIPResponse notAllowededResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null);
                        await transport.SendResponseAsync(notAllowededResponse);
                        break;
                    case SIPMethodsEnum.MESSAGE:
                        await do_message(sipRequest);
                        break;
                    default:

                        break;
                }
            }
            catch (Exception)
            {
                await send_bad(sipRequest);
            }
        }

        #region send code
        // 告知正在响铃
        internal async Task send_ringing(SIPRequest sipRequest)
        {
            var response = SIPResponse.GetResponse(
                sipRequest,
                SIPResponseStatusCodesEnum.Ringing,
                "Ringing");

            await transport.SendResponseAsync(response);
        }
        // 告知正在联系
        internal async Task send_trying(SIPRequest sipRequest)
        {
            var response = SIPResponse.GetResponse(
                sipRequest,
                SIPResponseStatusCodesEnum.Trying,
                "Trying");

            await transport.SendResponseAsync(response);
        }
        internal async Task send_bad(SIPRequest sipRequest)
        {
            var response = SIPResponse.GetResponse(
                sipRequest,
                SIPResponseStatusCodesEnum.BadRequest,
                "BadRequest");

            await transport.SendResponseAsync(response);
        }
        internal async Task send_busy(SIPRequest sipRequest)
        {
            var response = SIPResponse.GetResponse(
                sipRequest,
                SIPResponseStatusCodesEnum.BusyHere,
                "BusyHere");

            await transport.SendResponseAsync(response);
        }
        internal async Task send_timeout(SIPRequest sipRequest)
        {
            var response = SIPResponse.GetResponse(
                sipRequest,
                SIPResponseStatusCodesEnum.RequestTimeout,
                "RequestTimeout");

            await transport.SendResponseAsync(response);
        }
        internal async Task send_nofound(SIPRequest sipRequest)
        {
            var response = SIPResponse.GetResponse(
                sipRequest,
                SIPResponseStatusCodesEnum.NotFound,
                "NotFound");

            await transport.SendResponseAsync(response);
        }
        internal async Task send_unauthorised(SIPRequest sipRequest)
        {
            var response = SIPResponse.GetResponse(
                sipRequest,
                SIPResponseStatusCodesEnum.Unauthorised,
                "Unauthorised");

            await transport.SendResponseAsync(response);
        }
        //无人接听
        internal async Task send_unavailable(SIPRequest sipRequest)
        {
            var response = SIPResponse.GetResponse(
                sipRequest,
                SIPResponseStatusCodesEnum.TemporarilyUnavailable,
                "TemporarilyUnavailable");

            await transport.SendResponseAsync(response);
        }
        internal async Task send_ok(SIPRequest sipRequest)
        {
            var response = SIPResponse.GetResponse(
                sipRequest,
                SIPResponseStatusCodesEnum.Ok,
                "Ok");

            await transport.SendResponseAsync(response);
        }
        #endregion

        internal bool check_auth(SIPRequest sipRequest)
        {
            string sipUserName = sipRequest.Header.From.FromURI.User;
            var u = contact.FindByUser(sipUserName);
            if (u == null) return false;
            if (u.CallId != sipRequest.Header.CallId) return false;

            return true;
        }
        internal async Task do_register(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            string sipUserName = sipRequest.Header.From.FromURI.User;

            await send_trying(sipRequest);

            SIPResponseStatusCodesEnum registerResponse = SIPResponseStatusCodesEnum.Ok;
            if (sipRequest.Header.Contact?.Count > 0)
            {
                var expiry = sipRequest.Header.Contact[0].Expires > 0 ? sipRequest.Header.Contact[0].Expires : sipRequest.Header.Expires;

                var callid = sipRequest.Header.CallId;
                //注销;
                if (expiry <= 0)
                {
                    if (contact.RemoveByCallid(callid))
                        Console.WriteLine($"联系人 [{sipUserName}] 注销.");

                    return;
                }

                var sipAccount = new SIPAccount(sipUserName, sipRequest.Header.From.FromURI.Host);
                SIPAccountInfo binding = new SIPAccountInfo(sipRequest, sipAccount, sipRequest.Header.Contact[0].ContactURI, remoteEndPoint, localSIPEndPoint, expiry);

                contact.Add(sipUserName, binding);
                Console.WriteLine($"注册联系人 [{binding.RegisteredContact}] 为 [{sipAccount.SIPUsername}].");
            }
            else registerResponse = SIPResponseStatusCodesEnum.BadRequest;

            SIPNonInviteTransaction registerTransaction = new SIPNonInviteTransaction(transport, sipRequest, null);
            SIPResponse okResponse = SIPResponse.GetResponse(sipRequest, registerResponse, null);
            registerTransaction.SendResponse(okResponse);
        }
        internal async Task do_message(SIPRequest sipRequest)
        {
            await Task.CompletedTask;
            SIPNonInviteTransaction registerTransaction = new SIPNonInviteTransaction(transport, sipRequest, null);
            SIPResponse notImplResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotImplemented, null);
            registerTransaction.SendResponse(notImplResponse);
        }
        internal async Task do_bye(SIPRequest sipRequest)
        {
            var call = calling.RemoveByAccount(contact.FindFrom(sipRequest), contact.FindTo(sipRequest));
            if (call == null) goto ok_200;

            call.Close();
        ok_200:
            await send_ok(sipRequest);
            return;
        }
        internal async Task do_invite_req(SIPRequest sipRequest)
        {
            Console.WriteLine("invite");
            Console.WriteLine($"{sipRequest.Header.From.ToString()}");

            var call = calling.FindByCallId(sipRequest.Header.CallId);
            if (call != null)
            {
                await call.rtpSession.Start();
                goto ok;
            }

            call = new telephonist(this, sipRequest);
            if (!call.OK) goto err_480;
            calling.Insert(call);

            call.create_agent();
            if (!await call.call(120)) goto err_480;
        ok:
            await send_ok(sipRequest);
            return;
        err_480:
            await send_unavailable(sipRequest);
            return;
        }

        internal async Task do_invite_rep(SIPResponse sipResponse)
        {
            Console.WriteLine("00000000");
            var from = sipResponse.Header.From;
            var to = sipResponse.Header.To;
            string callId = sipResponse.Header.CallId;

            var accout = contact.FindByUser(sipResponse.Header.Contact?[0].ContactURI.User);
            if (accout == null)
            {
                Console.WriteLine("未找到合适的账户绑定,跳过.");
                return;
            }

            //保存相关信息供之后（如 BYE）使用;
            accout.CallId = sipResponse.Header.CallId;
            accout.FromTag = sipResponse.Header.From.FromTag;
            accout.ToTag = sipResponse.Header.To.ToTag;

            SIPRequest req = SIPRequest.GetRequest(SIPMethodsEnum.ACK, sipResponse.Header.To.ToURI,
                new SIPToHeader(to.ToName, to.ToURI, to.ToTag),
                new SIPFromHeader(@from.FromName, @from.FromURI, @from.FromTag));

            req.Header.Contact = new List<SIPContactHeader>() { new SIPContactHeader(sipResponse.Header.From.FromName, sipResponse.Header.From.FromURI) };
            req.Header.UserAgent = OpenTelConst.USER_AGENT;
            req.Header.Allow = null;
            req.Header.Vias = sipResponse.Header.Vias;
            req.Header.CallId = callId; //CallId 要和 200OK 的 CallId 一致，不然 IPC 不认;

            await transport.SendRequestAsync(accout.RemoteEndPoint, req);
        }
    }
}
